//------------------------------------------------------------------------------ 
//      Copyright (c) 2004-2009, Be Nanashi-San 
// 
//      Permission is hereby granted, free of charge, to any person obtaining a 
//      copy of this software and associated documentation files (the "Software"), 
//      to deal in the Software without restriction, including without limitation 
//      the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//      and/or sell copies of the Software, and to permit persons to whom the 
//      Software is furnished to do so, subject to the following conditions: 
// 
//      The above copyright notice and this permission notice shall be included in 
//      all copies or substantial portions of the Software. 
// 
//      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
//      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
//      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
//      FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
//      DEALINGS IN THE SOFTWARE. 
// 
//      File Name:   BoardOutLineListView.cpp
//      Author:      Be Nanashi-San 
//      Description: BBS Board list display class. 
//------------------------------------------------------------------------------

#include <Beep.h>
//#define DEBUG 1
#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <Mime.h>
#include <Window.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <String.h>

#include "2chPositive.h"
#include "BoardItem.h"
#include "BoardOutLineListView.h"
#include "Bookmark2ch.h"
#include "ThreadListView.h"
#include "Util.h"
#include "messages.h"
#include "setting.h"

static const unsigned int kNAMEBUFSIZ = 256;

////////////////////////////////////////////////////////////////////////////////
// BoardOutLineListView
//
//
////////////////////////////////////////////////////////////////////////////////
BoardOutLineListView::BoardOutLineListView()
: BOutlineListView("BoardList"),
fSeletion(NULL)
{
	BFont font;
	font.SetFamilyAndFace(theSettings().GetString(DISPLAY_BOARD_FONT_NAME), B_REGULAR_FACE);
	font.SetSize(atof(theSettings().GetString(DISPLAY_BOARD_FONT_SIZE)));
	this->SetFont(&font);
}

////////////////////////////////////////////////////////////////////////////////
// ~BoardOutLineListView
//
//
////////////////////////////////////////////////////////////////////////////////
BoardOutLineListView::~BoardOutLineListView()
{
	if(fSeletion) fSeletion->SetType(BOARD_NORMAL);

	for (;;){
		BoardItem *item = static_cast<BoardItem *>(BListView::RemoveItem(static_cast<int32>(0)));
		if (item == NULL) break;
		delete item;
	}
	
	this->MakeEmpty(); //必要？
}
////////////////////////////////////////////////////////////////////////////////
// AttachedToWindow
//
//
////////////////////////////////////////////////////////////////////////////////
void
BoardOutLineListView::AttachedToWindow() 
{ 
	BOutlineListView::AttachedToWindow(); 
} 

////////////////////////////////////////////////////////////////////////////////
// MessageReceived
//
//
////////////////////////////////////////////////////////////////////////////////
void
BoardOutLineListView::MessageReceived (BMessage * msg)
{
	switch (msg->what){
		// zeta 2 スレ@757のBe名無しさんより。
		printf("outlineview %ld\n", msg->what);
		case B_COPY_TARGET:
			DragActionCopy(msg);
			break;
		
		// zeta 2 スレ@757のBe名無しさんより。
		case B_SIMPLE_DATA:
			break;
			
		case kMSG_BOARDLIST_INVOCATION:
		{
			this->ItemInvoked(msg);
			break;
		}		
		case kMSG_FIND:
			if (FindBoardName(msg->FindString("findthis")) < 0)
				beep();
			break;

		default:
			BOutlineListView::MessageReceived (msg);
			break;
	}
}
////////////////////////////////////////////////////////////////////////////////
// ItemInvoked
//
//
////////////////////////////////////////////////////////////////////////////////
void
BoardOutLineListView::ItemInvoked(BMessage *message)
{
	int32 index = this->CurrentSelection();
	BoardItem	*item = static_cast<BoardItem *>(ItemAt(index));

	if (item->OutlineLevel() > 0) {
	//板が選択されたら一覧を開く。
		if(fSeletion) {
			fSeletion->SetType(BOARD_NORMAL);
			this->InvalidateItem(this->IndexOf(fSeletion));		
		}
		item->SetType(BOARD_SELECTED);
		this->InvalidateItem(index);
		fSeletion = item;
	
		BMessage openMessage(kMSG_OPEN_BOARD);
		openMessage.AddString("uri", item->Uri());
		if (message->FindString("dat"))
			openMessage.AddString("dat", message->FindString("dat"));
		ThreadListView* view = static_cast<ThreadListView*>(this->Window()->FindView("ThreadListView"));			
		BMessenger(view).SendMessage(&openMessage);
	}else{ 
	//グループ名が選択されたらリストの開閉。
		if(this->IsExpanded(this->FullListCurrentSelection())){
			this->Collapse(item);
		}else{
			this->Expand(item);
		}
	}
}
////////////////////////////////////////////////////////////////////////////////
// ExpandOrCollaspse
//
// すべてのリストを展開／省略
////////////////////////////////////////////////////////////////////////////////
void
BoardOutLineListView::ExpandOrCollaspse(bool expand)
{
	for(int i = 0; i < this->FullListCountItems(); i++){
		BListItem *item = this->FullListItemAt(i);
		if(expand)
			this->Expand(item);		
		else
			this->Collapse(item);
	}
}
////////////////////////////////////////////////////////////////////////////////
// InitiateDrag
//
// zeta 2 スレ@757のBe名無しさんより
////////////////////////////////////////////////////////////////////////////////
bool
BoardOutLineListView::InitiateDrag(BPoint /*point*/, int32 index, bool /*wasSelected*/)
{
	if (ItemAt(index)->OutlineLevel() == 0) return false;
	
	BRect frame(ItemFrame(index));
	BMessage msg(B_SIMPLE_DATA);
	//	add the types
	const char *m_bookmark_type = "application/x-vnd.Noname-2ch+-bookmark";
	msg.AddString("be:types", m_bookmark_type);
	msg.AddString("be:filetypes", m_bookmark_type);
	msg.AddString("be:types", B_FILE_MIME_TYPE);
	//	add the actions
	msg.AddInt32("be:actions", B_COPY_TARGET);
	msg.AddString("be:clip_name", static_cast<BoardItem *>(ItemAt(index))->Text());
	msg.AddString("url", static_cast<BoardItem *>(ItemAt(index))->Uri());
	msg.AddString("title", static_cast<BoardItem *>(ItemAt(index))->Text());
	PRINT(("Drag ")); 
	//PRINT_OBJECT(msg);
	DragMessage(&msg, frame);
	return true;
}

////////////////////////////////////////////////////////////////////////////////
// DragActionCopy
//
// zeta 2 スレ@757のBe名無しさんより
////////////////////////////////////////////////////////////////////////////////
void
BoardOutLineListView::DragActionCopy(BMessage * request)
{
	PRINT(("\nDragActionCopy "));
	//PRINT_OBJECT(&request);

	//	someone accepted our drag and requested one of the two
	//	types of data we can provide (in-message or in-file bitmap)
	const char * type = NULL;
	if (!request->FindString("be:types", &type) != B_OK) return;
	if (strcasecmp(type, B_FILE_MIME_TYPE)) return;

	const char *name, *data;
	ssize_t numBytes;
	entry_ref dir;
	if (!request->FindString("be:filetypes", &type) &&
			!request->FindString("name", &name) &&
			!request->FindData("_previous_", B_RAW_TYPE, reinterpret_cast<const void**>(&data), &numBytes) &&
			!request->FindRef("directory", &dir)) {
		BMessage archive;
		archive.Unflatten(data);
		BString url, title;
		if (!archive.FindString("url", &url) && !archive.FindString("title", &title)) {
			//	write file
			BDirectory d(&dir);
			BFile f(&d, name, O_RDWR | O_TRUNC);
			PRINT(("AddBookmarkFile(%s %s)\n", url.String(), title.String()));
			SetBookmarkAttr(&url, &title, &f);
		}
	}		
}
////////////////////////////////////////////////////////////////////////////////
// FindBoardName
// findとマッチするBoardItemを探す 
// zeta 2 スレ@713のBe名無しさんより。
////////////////////////////////////////////////////////////////////////////////
int32
BoardOutLineListView::FindBoardName(const char *find)
{
	BoardItem* item;
	const int32 currentSelection = this->FullListCurrentSelection();
	
	for (int32 index = currentSelection + 1; index < this->FullListCountItems(); index++) {
		item = static_cast<BoardItem *>(this->FullListItemAt(index));
		if (item->Text() && strstr(item->Text(), find))
			goto found;
	}
	for (int32 index = 0; index < currentSelection; index++) {
		item = static_cast<BoardItem *>(this->FullListItemAt(index));
		if (item->Text() && strstr(item->Text(), find))
			goto found;
	}
	return -2;	// not found

found:
	if (item->OutlineLevel()) {
		this->Expand(Superitem(item));
	}
	this->Select(this->IndexOf(item));
	this->ScrollToSelection();

	return this->IndexOf(item);
}
////////////////////////////////////////////////////////////////////////////////
// FindBoardUri
// zeta 2 スレ@713のBe名無しさんより。
////////////////////////////////////////////////////////////////////////////////
int32
BoardOutLineListView::FindBoardUri(const char *find)
{
	BoardItem* item;
	for (int32 index = 0; index < this->FullListCountItems(); index++) {
		item = static_cast<BoardItem *>(this->FullListItemAt(index));
		if (item->Uri() && strcmp(item->Uri(), find) == 0)
			goto found;
	}
	return -2;	// not found

found:
	if (item->OutlineLevel()) {
		this->Expand(Superitem(item));
	}
	this->Select(this->IndexOf(item));
	this->ScrollToSelection();

	return this->IndexOf(item);
}
////////////////////////////////////////////////////////////////////////////////
// CurrentQuery
//  Current ItemについてBMessageにして返す
//  zeta 2 スレ@757のBe名無しさんより。
////////////////////////////////////////////////////////////////////////////////
bool
BoardOutLineListView::CurrentQuery(BMessage *query)
{
	int32 index = this->CurrentSelection();
	if (index >=0 && ItemAt(index)->OutlineLevel()) {
		if(query){
			query->AddString("url", static_cast<BoardItem *>(ItemAt(index))->Uri());
			query->AddString("title", static_cast<BoardItem *>(ItemAt(index))->Text());
		}
		return true;
	}
	
	return false;
}
